---
title: Components and Props
description: "Basic concepts for components and props in ReScript & React"
canonical: "/docs/react/components-and-props"
section: "Main Concepts"
order: 3
---

# Components and Props

<Intro>

Components let you split the UI into independent, reusable pieces, and think about each piece in isolation. This page provides an introduction to the idea of components.

</Intro>

## What is a Component?

A React component is a function describing a UI element that receives a `props` object as a parameter (data describing the dynamic parts of the UI) and returns a `React.element`.

The nice thing about this concept is that you can solely focus on the input and output. The component function receives some data and returns some opaque `React.element` that is managed by the React framework to render your UI.

> If you want to know more about the low level details on how a component interface is implemented, refer to the [Beyond JSX](./beyond-jsx.mdx) page.

## Component Example

Let's start with a first example to see how a ReScript React component looks like:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// src/Greeting.res
@react.component
let make = () => {
  <div>
    {React.string("Hello ReScripters!")}
  </div>
}
```

```js
import * as React from "react";

function Greeting(props) {
  return JsxRuntime.jsx("div", {
    children: "Hello ReScripters!",
  });
}

var make = Greeting;
```

</CodeTab>

**Important:** Always make sure to name your component function `make`.

We've created a `Greeting.res` file that contains a `make` function that doesn't receive any props (the function doesn't receive any parameters), and returns a `React.element` that represents `<div> Hello ReScripters! </div>` in the rendered DOM.

You can also see in the the JS output that the function we created was directly translated into the pure JS version of a ReactJS component. Note how a `<div>` transforms into a `JsxRuntime.jsx("div",...)` call in JavaScript.

## Defining Props

In ReactJS, props are usually described as a single `props` record. In ReScript, we use [labeled arguments](../manual/function.mdx#labeled-arguments) to define our props parameters instead. Here's an example:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// src/Article.res
@react.component
let make = (~title: string, ~visitorCount: int, ~children: React.element) => {
  let visitorCountMsg = "You are visitor number: " ++ Belt.Int.toString(visitorCount);
  <div>
    <div> {React.string(title)} </div>
    <div> {React.string(visitorCountMsg)} </div>
    children
  </div>
}
```

```js
import * as React from "react";

function Article(props) {
  var visitorCountMsg = "You are visitor number: " + String(props.visitorCount);
  return React.createElement(
    "div",
    undefined,
    React.createElement("div", undefined, props.title),
    React.createElement("div", undefined, visitorCountMsg),
    props.children,
  );
}

var make = Article;
```

</CodeTab>

### Optional Props

We can leverage the full power of labeled arguments to define optional props as well:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// Greeting.res
@react.component
let make = (~name: option<string>=?) => {
  let greeting = switch name {
  | Some(name) => "Hello " ++ name ++ "!"
  | None => "Hello stranger!"
  }
  <div> {React.string(greeting)} </div>
}
```

```js
function Greeting(props) {
  var name = props.name;
  var greeting = name !== undefined ? "Hello " + name + "!" : "Hello stranger!";
  return React.createElement("div", undefined, greeting);
}
```

</CodeTab>

**Note:** The `@react.component` attribute implicitly adds the last `()` parameter to our `make` function for us (no need to do it ourselves).

In JSX, you can apply optional props with some special syntax:

<div className="hidden">

```res prelude
module Greeting = {
  @react.component
  let make = (~name: option<string>=?) => {React.string(name->Option.getOr(""))}
}
```

</div>

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let name = Some("Andrea")

<Greeting ?name />
```

```js
var name = "Andrea";

React.createElement(Greeting, {
  name: name,
});
```

</CodeTab>

### Special Props `key` and `ref`

You can't define any props called `key` or `ref`. React treats those props differently and the compiler will yield an error whenever you try to define a `~key` or `~ref` argument in your component function.

Check out the corresponding [Arrays and Keys](./arrays-and-keys.mdx) and [Forwarding React Refs](./forwarding-refs.mdx) sections for more details.

### Handling Invalid Prop Names (e.g. keywords)

Prop names like `type` (as in `<input type="text" />`) aren't syntactically valid; `type` is a reserved keyword in ReScript. Use `<input type_="text" />` instead.

For `aria-*` use camelCasing, e.g., `ariaLabel`. For DOM components, we'll translate it to `aria-label` under the hood.

For `data-*` this is a bit trickier; words with `-` in them aren't valid in ReScript. When you do want to write them, e.g., `<div data-name="click me" />`, check out the [React.cloneElement](./elements-and-jsx.mdx#cloning-elements) or [React.createDOMElementVariadic](./elements-and-jsx.mdx#creating-dom-elements) section.

## Children Props

In React `props.children` is a special attribute to represent the nested elements within a parent element:

```res
let element = <div> child1 child2 </div>
```

By default, whenever you are passing children like in the expression above, `children` will be treated
as a `React.element`:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
module MyList = {
  @react.component
  let make = (~children: React.element) => {
    <ul>
      children
    </ul>
  }
}

<MyList>
  <li> {React.string("Item 1")} </li>
  <li> {React.string("Item 2")} </li>
</MyList>
```

```js
function MyList(props) {
  return React.createElement("ul", undefined, props.children);
}

var MyList = {
  make: MyList,
};

React.createElement(
  MyList,
  {
    children: null,
  },
  React.createElement("li", undefined, "Item 1"),
  React.createElement("li", undefined, "Item 2"),
);
```

</CodeTab>

Interestingly, it doesn't matter if you are passing just one element, or several, React will always collapse its children to a single `React.element`.

It is also possible to redefine the `children` type as well. Here are some examples:

**Component with a mandatory `string` as children:**

```res
module StringChildren = {
  @react.component
  let make = (~children: string) => {
    <div>
      {React.string(children)}
    </div>
  }
}

<StringChildren> "My Child" </StringChildren>

// This will cause a type check error
<StringChildren/>
```

**Component with an optional `React.element` as children:**

```res example
module OptionalChildren = {
  @react.component
  let make = (~children: option<React.element>=?) => {
    <div>
      {switch children {
      | Some(element) => element
      | None => React.string("No children provided")
      }}
    </div>
  }
}

<div>
  <OptionalChildren />
  <OptionalChildren> <div /> </OptionalChildren>
</div>
```

**Component that doesn't allow children at all:**

```res
module NoChildren = {
  @react.component
  let make = () => {
    <div>
      {React.string("I don't accept any children params")}
    </div>
  }
}

// The compiler will raise a type error here
<NoChildren> <div/> </NoChildren>
```

Children props are really tempting to be abused as a way to model hierarchies, e.g. `<List> <ListHeader/> <Item/> </List>` (`List` should only allow `Item` / `ListHeader` elements), but this kind of constraint is hard to enforce because all components end up being a `React.element`, so it would require notorious runtime checking within `List` to verify that all children are in fact of type `Item` or `ListHeader`.

The best way to approach this kind of issue is by using props instead of children, e.g. `<List header="..." items=[{id: "...", text: "..."}]/>`. This way it's easy to type check the constraints, and it also spares component consumers from memorizing and remembering component constraints.

**The best use-case for `children` is to pass down `React.element`s without any semantic order or implementation details!**

## @react decorators

You might've wondered what `@react.component` actually does.
It's a decorator that tells the ReScript compiler to treat the function as a React component, transforming it at the syntax level.

In JavaScript, a React component is just a function that takes props (an object) and returns JSX. In ReScript, props are typically represented as a record type.
The `@react.component` decorator automatically generates that record type and wraps the function for you—so you don't have to.

```res
// Counter.res

@react.component
let make = (~title, ~count) => {
  <h1> {React.string(title)} {React.int(count)} </h1>
}

// This is equivalent to writing:

type props = {title: string, count: int}

let \"Counter" = ({title, count}: props) => {
  <h1> {React.string(title)} {React.int(count)} </h1>
}
```

However, writing it manually like this means you lose the `make` function name, which prevents JSX from working as expected when using the component elsewhere.

Having an uppercased function name also helps distinguish React components from regular functions in [React DevTools](https://react.dev/learn/react-developer-tools).

If you prefer defining your own props record, you can use `@react.componentWithProps`. This gives you full control over the props type while still generating a proper uppercased component.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
// Counter.res
type props = {title: string, count: int}

@react.componentWithProps
let make = (props: props) => {
  <h1>
    {React.string(props.title)}
    {React.int(props.count)}
  </h1>
}
```

```js
import * as JsxRuntime from "react/jsx-runtime";

function Counter(props) {
  return JsxRuntime.jsxs("h1", {
    children: [props.title, props.count],
  });
}

let make = Counter;
```

</CodeTab>

## Props & Type Inference

The ReScript type system is really good at inferring the prop types just by looking at its prop usage.

For simple cases, well-scoped usage, or experimentation, it's still fine to omit type annotations:

```res example
// Button.res

@react.component
let make = (~onClick, ~msg, ~children) => {
  <div onClick>
    {React.string(msg)}
    children
  </div>
}
```

In the example above, `onClick` will be inferred as `ReactEvent.Mouse.t => unit`, `msg` as `string` and `children` as `React.element`. Type inference is especially useful when you just forward values to some smaller (privately scoped) functions.

Even though type inference spares us a lot of keyboard typing, we still recommend to explicitly type your props (just like with any public API) for better type visibility and to prevent confusing type errors.

## Using Components in JSX

Every ReScript component can be used in JSX. For example, if we want to use our `Greeting` component within our `App` component, we can do this:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
// src/App.res

@react.component
let make = () => {
  <div>
    <Greeting/>
  </div>
}
```

```js
var React = require("react");
var Greeting = require("./Greeting.js");

function App(Props) {
  return React.createElement(
    "div",
    undefined,
    React.createElement(Greeting.make, {}),
  );
}

var make = App;
```

</CodeTab>

**Note:** React components are capitalized; primitive DOM elements like `div` or `button` are uncapitalized. More infos on the JSX specifics and code transformations can be found in our [JSX language manual section](../manual/jsx.mdx#capitalized-tag).

### Handwritten Components

You don't need to use the `@react.component` decorator to write components that can be used in JSX. Instead you can write the `make` function with type `props` and these will always work as React components. But then you will have the issue with the component name being "make" in the React dev tools.

For example:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
module Link = {
  type props = {href: string, children: React.element};

  let make = (props: props) => {
    <a href={props.href}>
     {props.children}
    </a>
  }
}

<Link href="/docs"> {React.string("Docs")} </Link>
```

```js
function make(props) {
  return React.createElement("a", { href: props.href }, props.children);
}

var Link = {
  make: make,
};

React.createElement(make, {
  href: "/docs",
  children: "Docs",
});
```

</CodeTab>

More details on the `@react.component` decorator and its generated interface can be found in our [Beyond JSX](./beyond-jsx.mdx) page.

## Submodule Components

We can also represent React components as submodules, which makes it very convenient to build more complex UI without the need to create multiple files for each composite component (that's probably only used by the parent component anyways):

```res example
// src/Button.res
module Label = {
  @react.component
  let make = (~title: string) => {
    <div className="myLabel"> {React.string(title)} </div>
  }
}

@react.component
let make = (~children) => {
  <div>
    <Label title="Getting Started" />
    children
  </div>
}
```

The `Button.res` file defined above is now containing a `Label` component, that can also be used by other components, either by writing the fully qualified module name (`<Button.Label title="My Button"/>`) or by using a module alias to shortcut the full qualifier:

```res
module Label = Button.Label

let content = <Label title="Test"/>
```

## Component Naming

Because components are actually a pair of functions, they have to belong to a module to be used in JSX. It makes sense to use these modules for identification purposes as well. `@react.component` or `@react.componentWithProps` automatically adds the name for you based on the module you are in.

```res
// File.res

// will be named `File` in dev tools
@react.component
let make = ...

// will be named `File$component` in dev tools
@react.component
let component = ...

module Nested = {
  // will be named `File$Nested` in dev tools
  @react.component
  let make = ...
}
```

If you need a dynamic name for higher-order components or you would like to set your own name you can use `React.setDisplayName(make, "NameThatShouldBeInDevTools")`.

## Tips & Tricks

- Start with one component file and utilize submodule components as your component grows. Consider splitting up in multiple files when really necessary.
- Keep your directory hierarchy flat. Instead of `article/Header.res` use `ArticleHeader.res` etc. Filenames are unique across the codebase, so filenames tend to be very specific `ArticleUserHeaderCard.res`, which is not necessarily a bad thing, since it clearly expresses the intent of the component within its name, and makes it also very easy to find, match and refactor across the whole codebase.
